﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Bankeren.Engine.Cards
{
    /// <summary>
    /// Represents a deck of playingcards.
    /// </summary>
    public class Deck : IEnumerable<Card>
    {
        #region Fields

        private static Random rnd = new Random();

        private List<Card> cards;
        private bool hasBeenFilled;

        #endregion

        #region Properties

        /// <summary>
        /// Number of cards left in the deck.
        /// </summary>
        public int CardsLeft
        {
            get
            {
                return this.cards.Count;
            }
        }

        /// <summary>
        /// Indexer for this deck.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <returns>Card at the given index.</returns>
        public Card this[int index]
        {
            get
            {
                return this.cards[index];
            }
        }

        #endregion

        #region Ctor

        /// <summary>
        /// Public constructor for deck.
        /// </summary>
        public Deck()
        {
            this.cards = new List<Card>();
            this.hasBeenFilled = false;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Fills the deck with the standard 52 cards.
        /// </summary>
        public void Fill()
        {
            if (!hasBeenFilled)
            {
                foreach (Suit suit in Suit.List)
                {
                    foreach (Rank rank in Rank.List)
                    {
                        this.cards.Add(new Card(suit, rank));
                    }
                }
                this.hasBeenFilled = true;
            }
            else
                throw new InvalidOperationException("Cannot fill a deck that has already been filled. " +
                                                    "Please draw all cards before filling the deck again.");
        }

        /// <summary>
        /// Shuffles the card in the deck with the Fisher-Yates algorithm.
        /// </summary>
        public void Shuffle()
        {
            if (this.CardsLeft > 0)
            {
                Random random = rnd;
                for (int i = cards.Count; i > 1; i--)
                {
                    // Pick random element to swap.
                    int j = random.Next(i); // 0 <= j <= i-1
                    // Swap.
                    Card tmp = cards[j];
                    cards[j] = cards[i - 1];
                    cards[i - 1] = tmp;
                }
            }
            else
                throw new InvalidOperationException("Cannot shuffle an empty deck.");
        }

        /// <summary>
        /// Fills and shuffles the deck.
        /// </summary>
        public void FillAndShuffle()
        {
            this.Fill();
            this.Shuffle();
        }

        /// <summary>
        /// Draws the topmost card of the deck.
        /// </summary>
        /// <returns>The topmost card of the deck.</returns>
        public Card Draw()
        {
            if (this.CardsLeft > 0)
            {
                Card tmp = this.cards[0];
                this.cards.Remove(tmp);
                if (this.CardsLeft == 0)
                    this.hasBeenFilled = false;
                return tmp;
            }
            else
                throw new InvalidOperationException("Cannot draw from an empty deck.");
        }

        /// <summary>
        /// Gets an Enumerator for this deck.
        /// </summary>
        /// <returns>An Enumerator for this deck.</returns>
        public IEnumerator<Card> GetEnumerator()
        {
            return this.cards.GetEnumerator();
        }

        /// <summary>
        /// Gets an Enumerator for this deck.
        /// </summary>
        /// <returns>An Enumerator for this deck.</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.cards.GetEnumerator();
        }

        #endregion
    }
}
